package com.whc.gulimall.order.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rabbitmq.client.Channel;
import com.whc.common.to.OrderTO;
import com.whc.common.to.mq.SecKillTO;
import com.whc.common.utils.PageUtils;
import com.whc.common.utils.Query;
import com.whc.common.utils.R;
import com.whc.common.utils.RRException;
import com.whc.common.vo.SocialResponseVo;
import com.whc.gulimall.order.constant.AliPayConstant;
import com.whc.gulimall.order.constant.CartConstant;
import com.whc.gulimall.order.constant.OrderConstant;
import com.whc.gulimall.order.constant.OrderMQConstant;
import com.whc.gulimall.order.dao.OrderDao;
import com.whc.gulimall.order.entity.OrderEntity;
import com.whc.gulimall.order.entity.OrderItemEntity;
import com.whc.gulimall.order.entity.OrderReturnReasonEntity;
import com.whc.gulimall.order.entity.PaymentInfoEntity;
import com.whc.gulimall.order.enume.OrderStatusEnum;
import com.whc.gulimall.order.feign.CartFeignService;
import com.whc.gulimall.order.feign.MemberFeignService;
import com.whc.gulimall.order.feign.ProductFeignService;
import com.whc.gulimall.order.feign.WareFeignService;
import com.whc.gulimall.order.interceptor.OrderInterceptor;
import com.whc.gulimall.order.service.OrderItemService;
import com.whc.gulimall.order.service.OrderService;
import com.whc.gulimall.order.service.PaymentInfoService;
import com.whc.gulimall.order.to.OrderCreateTo;
import com.whc.gulimall.order.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @author angaoen
 */
//@RabbitListener(queues = {"gulimall-queue"})
@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    /**
     * 会员相关远程接口
     */
    @Resource
    private MemberFeignService memberFeignService;

    /**
     * 商品相关远程接口
     */
    @Resource
    private CartFeignService cartFeignService;

    /**
     * 以字符串为中心的模板扩展。
     */
    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    @Resource
    private ThreadPoolExecutor executors;

    /**
     * 仓库远程调用
     */
    @Resource
    private WareFeignService wareFeignService;

    /**
     * 商品远程调用
     */
    @Resource
    private ProductFeignService productFeignService;

    /**
     * 订单项信息
     */
    @Resource
    private OrderItemService orderItemService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private PaymentInfoService paymentInfoService;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 获取订单信息
     *
     * @return 订单信息
     */
    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        SocialResponseVo socialResponseVo = OrderInterceptor.threadLocal.get();
        // 登录用户才有id
        if (socialResponseVo.getId() != null) {
            OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

            CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                // 获取收货地址
                R r = memberFeignService.listReceiveAddressByMemberId(socialResponseVo.getId());
                if (r.getCode() == 0) {
                    List<MemberReceiveAddressVo> receiveAddresses = r
                            .getData(new TypeReference<List<MemberReceiveAddressVo>>() {
                            });
                    orderConfirmVo.setAddressList(receiveAddresses);
                }
            }, executors);

            CompletableFuture<Void> productFuture = CompletableFuture.runAsync(() -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                // 获取购物车商品
                R r = cartFeignService.listProductList();
                if (r.getCode() == 0) {
                    List<ProductVo> productVos = r.getData(new TypeReference<List<ProductVo>>() {
                    });
                    orderConfirmVo.setProductList(productVos);
                }
            }, executors);

            CompletableFuture.allOf(addressFuture, productFuture).get();

            // 防重令牌
            String orderToken = UUID.fastUUID().toString().replaceAll("-", "");
            // 设置防重令牌到本地
            redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN_PREFIX + socialResponseVo.getId(), orderToken);
            orderConfirmVo.setOrderToken(orderToken);

            return orderConfirmVo;
        }
        return null;
    }

    /**
     * 提交订单
     *
     * @param submitVo 订单提交信息vo
     * @return 订单结果vo
     */
    @Override
    public SubmitOrderResponseVo summitOrder(OrderSubmitVo submitVo) throws ExecutionException, InterruptedException {
        SocialResponseVo socialResponseVo = OrderInterceptor.threadLocal.get();
        String orderToken = submitVo.getOrderToken();
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1])" +
                " else return 0 end";
        Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(OrderConstant.ORDER_TOKEN_PREFIX + socialResponseVo.getId()), orderToken);

        OrderCreateTo order = createOrder(submitVo.getAttrId());
        if (Objects.equals(1L, result)) {
            responseVo.setOrderEntity(order.getOrder());
            // 验价
            if (Math.abs(order.getOrder().getPayAmount()
                    .subtract(responseVo.getOrderEntity().getPayAmount()).doubleValue()) < 0.01) {
                // 保存订单
                saveOrder(order);
                // 锁库存
                WareLockVo wareLockVo = new WareLockVo();
                wareLockVo.setOrderSn(order.getOrder().getOrderSn());
                wareLockVo.setProductVos(order.getOrderItems().stream().map(item -> {
                    ProductLockVo productLockVo = new ProductLockVo();
                    productLockVo.setCount(item.getSkuQuantity());
                    productLockVo.setSkuId(item.getSkuId());
                    productLockVo.setTitle(item.getSkuName());
                    return productLockVo;
                }).collect(Collectors.toList()));

                // 调用远程接口，锁定库存
                R r = wareFeignService.orderLockedStock(wareLockVo);
                if (Objects.equals(0, r.getCode())) {
                    responseVo.setOrderEntity(order.getOrder());
                    responseVo.setCode(0);
                    // 订单成功，发送给mq

                    rabbitTemplate.convertAndSend(OrderMQConstant.ORDER_EVENT_EXCHANGE, "order.create.order",
                            order.getOrder());
                    return responseVo;
                } else {
                    log.error("wareFeignService.orderLockedStock(wareLockVo)");
                    throw new RRException("订单异常");
                }
            }
        }
        // 对比失败
        responseVo.setCode(1);
        return responseVo;
    }

    /**
     * 通过orderSN获取订单状态
     *
     * @param orderSN orderSN
     * @return 订单状态
     */
    @Override
    public Integer getOrderStatusByOrderSN(String orderSN) {
        return baseMapper.selectOrderStatusByOrderSN(orderSN);
    }

    /**
     * 关闭订单
     *
     * @param entity 订单信息
     */
    @Override
    public void closeOrder(OrderEntity entity) {
        OrderEntity orderEntity = this.getById(entity.getId());
        // 三十分钟过后，仍然处于待付款，就需要撤销下单
        if (orderEntity.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())) {
            OrderEntity update = new OrderEntity();
            update.setStatus(OrderStatusEnum.CANCLED.getCode());
            update.setId(orderEntity.getId());
            // 关单
            this.updateById(update);
            // 发送mq，通知关单成功
            OrderTO orderTO = new OrderTO();
            BeanUtil.copyProperties(entity, orderTO);
            rabbitTemplate.convertAndSend(OrderMQConstant.ORDER_EVENT_EXCHANGE, "stock.release.other",
                    orderTO);
        }
    }

    @Override
    public PayVo createPayVO(String orderSn) {
        // 通过orderSn获取到订单信息
        OrderItemEntity itemEntity = orderItemService.list(new QueryWrapper<OrderItemEntity>()
                .eq("order_sn", orderSn)).get(0);
        return new PayVo(orderSn, itemEntity.getSkuName(),
                this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn)).getPayAmount()
                        .setScale(2, RoundingMode.UP).toString(), itemEntity.getSkuAttrsVals());
    }

    @Override
    public PageUtils listOrderItem(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id", OrderInterceptor.threadLocal.get().getId())
                        .orderByDesc("modify_time")
        );

        List<OrderEntity> entities = page.getRecords();
        if (CollUtil.isNotEmpty(entities)) {
            List<String> orderSnList = entities.stream().map(OrderEntity::getOrderSn).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(orderSnList)) {
                Map<String, List<OrderItemEntity>> itemEntityMap = orderItemService.mapItemByOrderSn(orderSnList);
                for (OrderEntity entity : entities) {
                    entity.setItemEntityList(itemEntityMap.get(entity.getOrderSn()));
                }
            }
        }
        page.setRecords(entities);

        return new PageUtils(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String handleAliPayed(PayAsyncVo vo) {
        PaymentInfoEntity infoEntity = new PaymentInfoEntity();
        infoEntity.setAlipayTradeNo(vo.getTrade_no());
        infoEntity.setOrderSn(vo.getOut_trade_no());
        infoEntity.setPaymentStatus(vo.getTrade_status());
        infoEntity.setCallbackTime(vo.getNotify_time());
        paymentInfoService.save(infoEntity);

        // 修改状态
        if (AliPayConstant.TRADE_SUCCESS.equals(vo.getTrade_status())
                || AliPayConstant.TRADE_FINISHED.equals(vo.getTrade_status())) {
            baseMapper.updateStatusByOrderSn(vo.getOut_trade_no(), OrderStatusEnum.PAYED.getCode());
        }
        return "success";
    }

    /**
     * 创建秒杀订单
     *
     * @param to 订单信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createKillOrder(SecKillTO to)   {
        // 创建订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(to.getOrderSn());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setMemberId(to.getMemberId());
        orderEntity.setTotalAmount(to.getSeckillPrice().multiply(new BigDecimal(to.getNum())));
        orderEntity.setPayAmount(to.getSeckillPrice().multiply(new BigDecimal(to.getNum())));
        boolean flag1 = this.save(orderEntity);

        Long skuId = to.getSkuId();
        OrderItemEntity itemEntity = new OrderItemEntity();
        itemEntity.setOrderSn(to.getOrderSn());
        itemEntity.setOrderId(orderEntity.getId());
        itemEntity.setSkuPrice(to.getSeckillPrice());
        itemEntity.setSkuQuantity(to.getNum());

        boolean flag2 = orderItemService.save(itemEntity);
        if (flag1 == flag2 && flag1) {
            // 延时队列，关单
            rabbitTemplate.convertAndSend(OrderMQConstant.ORDER_EVENT_EXCHANGE,
                    "order.create.order", orderEntity);
        }
    }

    /**
     * 保存订单
     *
     * @param order 订单信息
     */
    public void saveOrder(OrderCreateTo order) {
        // 保存订单信息
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        int result = baseMapper.insert(orderEntity);
        if (result == 0) {
            log.error("saveOrder(OrderCreateTo order)->{}", "保存订单失败");
            throw new RuntimeException("保存订单失败");
        }

        // 保存订单商品信息
        List<OrderItemEntity> orderItems = order.getOrderItems();
        Long orderId = orderEntity.getId();
        // 设置订单主键
        orderItems.forEach(item -> item.setOrderId(orderId));
        boolean flag = orderItemService.saveBatch(orderItems);
        if (!flag) {
            log.error("saveOrder(OrderCreateTo order)->{}", "批量保存订单详情失败");
            throw new RuntimeException("批量保存订单详情失败");
        }
    }

    /**
     * 创建订单
     *
     * @param attrId 收货地址id
     * @return 订单信息
     */
    private OrderCreateTo createOrder(Long attrId) throws ExecutionException, InterruptedException {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        // 生成订单号
        String orderSn = IdWorker.getTimeId();

        SocialResponseVo socialResponseVo = OrderInterceptor.threadLocal.get();

        OrderEntity orderEntity = buildOrder(attrId, orderSn, socialResponseVo);
        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setFare(orderEntity.getFreightAmount());

        R r = cartFeignService.listProductList();
        if (Objects.equals(r.getCode(), 0)) {
            List<ProductVo> productVoList = r.getData(new TypeReference<List<ProductVo>>() {
            });
            if (CollUtil.isNotEmpty(productVoList)) {
                List<OrderItemEntity> orderItemEntities = buildOrderItems(productVoList, orderSn);
                r = productFeignService.mapProductInfos(orderItemEntities.stream()
                        .map(OrderItemEntity::getSkuId).collect(Collectors.toList()));
                if (r.getCode() == 0) {
                    Map<Long, ProductInfoVo> productInfoVoMap = r.getData(new TypeReference<Map<Long, ProductInfoVo>>() {
                    });
                    if (productInfoVoMap != null) {
                        orderItemEntities.forEach(item ->
                                BeanUtil.copyProperties(productInfoVoMap.get(item.getSkuId()), item));
                    }
                }
                // 设置订单商品信息
                orderCreateTo.setOrderItems(orderItemEntities);
                // 计算价格
                computePrice(orderEntity, orderItemEntities);
            }
        }
        orderCreateTo.setPayPrice(orderEntity.getPayAmount());
        return orderCreateTo;
    }

    /**
     * 计算价格
     *
     * @param orderEntity 订单
     * @param orderItems  订单商品信息
     */
    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItems) {
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal promotionAmount = BigDecimal.ZERO;
        BigDecimal integrationAmount = BigDecimal.ZERO;
        BigDecimal couponAmount = BigDecimal.ZERO;

        for (OrderItemEntity orderItem : orderItems) {
            totalAmount = totalAmount.add(orderItem.getRealAmount());
            promotionAmount = promotionAmount.add(orderItem.getPromotionAmount());
            integrationAmount = integrationAmount.add(orderItem.getIntegrationAmount());
            couponAmount = couponAmount.add(orderItem.getCouponAmount());
        }

        orderEntity.setTotalAmount(totalAmount);
        orderEntity.setPromotionAmount(promotionAmount);
        orderEntity.setIntegrationAmount(integrationAmount);
        orderEntity.setCouponAmount(couponAmount);
        orderEntity.setPayAmount(orderEntity.getTotalAmount().add(orderEntity.getFreightAmount()));
    }

    /**
     * 构建订单商品信息
     *
     * @param productVoList 商品列表
     * @param orderSn       订单编号
     * @return List<OrderItemEntity>订单列表信息
     */
    private List<OrderItemEntity> buildOrderItems(List<ProductVo> productVoList, String orderSn) {
        return productVoList.stream().map(item -> {
            OrderItemEntity orderItemEntity = new OrderItemEntity();
            orderItemEntity.setOrderSn(orderSn);
            orderItemEntity.setSkuId(item.getSkuId());
            orderItemEntity.setSkuName(item.getTitle());
            orderItemEntity.setSkuPic(item.getImage());
            orderItemEntity.setSkuQuantity(item.getCount());
            orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(item.getSkuAttr(), ";"));
            orderItemEntity.setRealAmount(item.getPrice()
                    .multiply(new BigDecimal(item.getCount() + ""))
                    .subtract(orderItemEntity.getPromotionAmount()).subtract(orderItemEntity.getCouponAmount())
                    .subtract(orderItemEntity.getIntegrationAmount()));
            return orderItemEntity;
        }).collect(Collectors.toList());
    }

    /**
     * 构建订单信息
     *
     * @param attrId           地址id
     * @param orderSn          订单编号
     * @param socialResponseVo 社交媒体信息
     * @return 订单信息
     */
    private OrderEntity buildOrder(Long attrId, String orderSn, SocialResponseVo socialResponseVo) throws ExecutionException, InterruptedException {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderSn);
        orderEntity.setMemberId(socialResponseVo.getId());
        orderEntity.setMemberUsername(socialResponseVo.getUsername());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setDeleteStatus(0);

        CompletableFuture<Void> setAddressFuture = CompletableFuture.runAsync(() -> {
            R r = memberFeignService.info(attrId);
            if (Objects.equals(0, r.getCode())) {
                MemberReceiveAddressVo memberReceiveAddressVo = r.getData(new TypeReference<MemberReceiveAddressVo>() {
                });
                if (memberReceiveAddressVo != null) {
                    orderEntity.setReceiverCity(memberReceiveAddressVo.getCity());
                    orderEntity.setReceiverDetailAddress(memberReceiveAddressVo.getDetailAddress());
                    orderEntity.setReceiverName(memberReceiveAddressVo.getName());
                    orderEntity.setReceiverPhone(memberReceiveAddressVo.getPhone());
                    orderEntity.setReceiverPostCode(memberReceiveAddressVo.getPostCode());
                    orderEntity.setReceiverRegion(memberReceiveAddressVo.getRegion());
                    orderEntity.setReceiverProvince(memberReceiveAddressVo.getProvince());
                }
            }
        }, executors);

        CompletableFuture<Void> setFareFuture = CompletableFuture.runAsync(() -> {
            R r = wareFeignService.getAddressFareByAddrId(attrId);
            if (r.getCode() == 0) {
                orderEntity.setFreightAmount(r.getData(new TypeReference<BigDecimal>() {
                }));
            }
        }, executors);

        CompletableFuture.allOf(setAddressFuture, setFareFuture).get();
        return orderEntity;
    }

    /**
     * 根据userId获取到hash操作对象
     *
     * @param userId userId
     * @return hash操作对象
     */
    private BoundHashOperations<String, Object, Object> getHashOps(Long userId) {
        return redisTemplate.boundHashOps(CartConstant.CART_PREFIX + userId);
    }


    @RabbitHandler
    public void received(Message message, OrderReturnReasonEntity entity, Channel channel) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            if (deliveryTag % 2 == 0) {
                channel.basicAck(deliveryTag, false);
            } else {
                channel.basicNack(deliveryTag, false, true);
            }

        } catch (IOException e) {

        }
        log.info("received(OrderReturnReasonEntity entity)=>{}", entity);
        log.info("deliveryTag=>{}", deliveryTag);
    }

    @RabbitHandler
    public void received(Message message, OrderEntity entity, Channel channel) {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        log.info("received(OrderEntity entity)=>{}", entity);
        try {
            channel.basicAck(deliveryTag, false);
        } catch (IOException e) {

        }
    }

}
